package com.note.app.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.note.app.annotation.LogRecord;
import com.note.app.dto.log.RequestLogDTO;
import com.note.app.service.LogService;
import com.note.common.context.UserContext;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * 日志记录切面
 * 拦截带有@LogRecord注解的方法，记录请求和响应信息
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class LogRecordAspect {

    private final LogService logService;
    private final ObjectMapper objectMapper;
    
    /**
     * 定义切点 - 拦截带有@LogRecord注解的方法
     */
    @Pointcut("@annotation(com.note.app.annotation.LogRecord)")
    public void logRecordPointcut() {
    }
    
    /**
     * 环绕通知 - 记录方法执行前后的信息
     */
    @Around("logRecordPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 创建请求日志记录
        RequestLogDTO logDTO = new RequestLogDTO();
        
        // 记录请求时间
        LocalDateTime requestTime = LocalDateTime.now();
        logDTO.setRequestTime(requestTime);
        
        // 生成唯一请求ID
        logDTO.setRequestId(UUID.randomUUID().toString());
        
        // 获取当前请求
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        
        // 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogRecord logRecord = method.getAnnotation(LogRecord.class);
        
        // 设置操作信息
        logDTO.setOperationType(logRecord.operationType());
        logDTO.setModule(logRecord.module());
        logDTO.setDescription(logRecord.description());
        
        // 记录请求信息
        logDTO.setRequestUri(request.getRequestURI());
        logDTO.setRequestMethod(request.getMethod());
        
        // 记录请求参数
        if (logRecord.recordRequestParams()) {
            try {
                // 记录URL参数
                logDTO.setRequestParams(request.getParameterMap());
                
                // 记录请求体
                Object[] args = joinPoint.getArgs();
                if (args != null && args.length > 0) {
                    // 过滤掉HttpServletRequest等非JSON可序列化对象
                    Object[] filteredArgs = filterArgs(args);
                    if (filteredArgs.length > 0) {
                        logDTO.setRequestBody(objectMapper.writeValueAsString(
                                filteredArgs.length == 1 ? filteredArgs[0] : filteredArgs
                        ));
                    }
                }
            } catch (Exception e) {
                log.warn("记录请求参数失败: {}", e.getMessage());
            }
        }
        
        // 记录请求头
        try {
            Map<String, String> headers = new HashMap<>();
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                // 排除一些敏感头信息如Cookie、Authorization等
                if (!"cookie".equalsIgnoreCase(headerName) && 
                    !"authorization".equalsIgnoreCase(headerName)) {
                    headers.put(headerName, request.getHeader(headerName));
                }
            }
            logDTO.setHeaders(headers);
        } catch (Exception e) {
            log.warn("记录请求头失败: {}", e.getMessage());
        }
        
        // 记录客户端信息
        logDTO.setClientIp(getClientIp(request));
        logDTO.setUserAgent(request.getHeader("User-Agent"));
        
        // 记录用户信息
        if (logRecord.recordUserInfo()) {
            try {
                Long userId = UserContext.getUserId();
                if (userId != null) {
                    logDTO.setUserId(userId);
                    // 这里可以通过userId获取更多用户信息
                    logDTO.setUsername(UserContext.getUsername());
                }
            } catch (Exception e) {
                log.warn("获取用户信息失败: {}", e.getMessage());
            }
        }
        
        // 执行方法并记录响应
        Object result = null;
        try {
            // 执行原方法
            result = joinPoint.proceed();
            
            // 记录响应时间
            LocalDateTime responseTime = LocalDateTime.now();
            logDTO.setResponseTime(responseTime);
            
            // 计算处理时间
            logDTO.setProcessingTime(Duration.between(requestTime, responseTime).toMillis());
            
            // 记录响应状态码 (这里假设成功为200)
            logDTO.setResponseStatus(200);
            
            // 记录响应体
            if (logRecord.recordResponseData() && result != null) {
                try {
                    logDTO.setResponseBody(objectMapper.writeValueAsString(result));
                } catch (Exception e) {
                    log.warn("记录响应体失败: {}", e.getMessage());
                }
            }
        } catch (Throwable e) {
            // 记录响应时间
            LocalDateTime responseTime = LocalDateTime.now();
            logDTO.setResponseTime(responseTime);
            
            // 计算处理时间
            logDTO.setProcessingTime(Duration.between(requestTime, responseTime).toMillis());
            
            // 记录响应状态码 (这里假设异常为500)
            logDTO.setResponseStatus(500);
            
            // 记录异常信息
            if (logRecord.recordException()) {
                logDTO.setExceptionMessage(e.getMessage());
                // 记录异常堆栈 (简化处理，实际可能需要限制长度)
                StringBuilder stackTrace = new StringBuilder();
                for (StackTraceElement stackTraceElement : e.getStackTrace()) {
                    stackTrace.append(stackTraceElement).append("\n");
                    if (stackTrace.length() > 1000) {
                        stackTrace.append("... (more)");
                        break;
                    }
                }
                logDTO.setExceptionStack(stackTrace.toString());
            }
            
            // 重新抛出异常
            throw e;
        } finally {
            // 异步记录日志
            logService.recordRequestLog(logDTO);
        }
        
        return result;
    }
    
    /**
     * 过滤参数 - 排除不能序列化为JSON的对象
     */
    private Object[] filterArgs(Object[] args) {
        return java.util.Arrays.stream(args)
                .filter(arg -> !(arg instanceof HttpServletRequest))
                .filter(Objects::nonNull)
                .toArray();
    }
    
    /**
     * 获取客户端IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        
        // 多个代理的情况，第一个IP为客户端真实IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        
        return ip;
    }
} 